{{>licenseInfo}}
{{#models}}{{#model}}

#include "{{classname}}.h"

#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <stdexcept>
#include <regex>
{{#hasEnums}}
#include <algorithm>
{{/hasEnums}}
#include <boost/lexical_cast.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "helpers.h"

using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;

{{#modelNamespaceDeclarations}}
namespace {{this}} {
{{/modelNamespaceDeclarations}}

{{classname}}::{{classname}}(boost::property_tree::ptree const& pt)
{
        fromPropertyTree(pt);
}


std::string {{classname}}::toJsonString(bool prettyJson /* = false */) const
{
	std::stringstream ss;
	write_json(ss, this->toPropertyTree(), prettyJson);
    // workaround inspired by: https://stackoverflow.com/a/56395440
    std::regex reg("\\\"([0-9]+\\.{0,1}[0-9]*)\\\"");
    std::string result = std::regex_replace(ss.str(), reg, "$1");
    return result;
}

void {{classname}}::fromJsonString(std::string const& jsonString)
{
	std::stringstream ss(jsonString);
	ptree pt;
	read_json(ss,pt);
	this->fromPropertyTree(pt);
}

ptree {{classname}}::toPropertyTree() const
{
	ptree pt;
	ptree tmp_node;
	{{#vars}}
	{{^isContainer}}
	{{#isPrimitiveType}}
	pt.put("{{baseName}}", m_{{name}});
	{{/isPrimitiveType}}
	{{^isPrimitiveType}}
	{{#isString}}
	pt.put("{{baseName}}", m_{{name}});
	{{/isString}}
	{{#isDate}}
	pt.put("{{baseName}}", m_{{name}});
	{{/isDate}}
	{{#isDateTime}}
	pt.put("{{baseName}}", m_{{name}});
	{{/isDateTime}}
	{{#isModel}}
	pt.add_child("{{baseName}}", m_{{name}}.toPropertyTree());
	{{/isModel}}
	{{/isPrimitiveType}}
	{{/isContainer}}
	{{#isContainer}}
	// generate tree for {{name}}
    {{#isMap}}
    {{^isModel}}
    if (!m_{{name}}.empty()) {
        tmp_node = toPt(m_{{name}});
        pt.add_child("{{baseName}}", tmp_node);
    }
    {{/isModel}}
    {{/isMap}}
    tmp_node.clear();
    {{^isMap}}
	if (!m_{{name}}.empty()) {
        tmp_node = toPt(m_{{name}});
		pt.add_child("{{baseName}}", tmp_node);
		tmp_node.clear();
	}
    {{/isMap}}
	{{/isContainer}}
	{{/vars}}
	return pt;
}

void {{classname}}::fromPropertyTree(ptree const &pt)
{
	ptree tmp_node;
	{{#vars}}
	{{^isContainer}}
	{{^isEnum}}
	{{#isPrimitiveType}}
	m_{{name}} = pt.get("{{baseName}}", {{{defaultValue}}});
	{{/isPrimitiveType}}
	{{^isPrimitiveType}}
	{{#isString}}
	m_{{name}} = pt.get("{{baseName}}", {{{defaultValue}}});
	{{/isString}}
	{{#isDate}}
	m_{{name}} = pt.get("{{baseName}}", {{{defaultValue}}});
	{{/isDate}}
	{{#isDateTime}}
	m_{{name}} = pt.get("{{baseName}}", {{{defaultValue}}});
	{{/isDateTime}}
	{{/isPrimitiveType}}
	{{/isEnum}}
	{{#isEnum}}
	{{setter}}(pt.get("{{baseName}}", {{{defaultValue}}}));
	{{/isEnum}}
	{{#isModel}}
	if (pt.get_child_optional("{{baseName}}")) {
        m_{{{name}}} = fromPt<{{{dataType}}}>(pt.get_child("{{baseName}}"));
	}
	{{/isModel}}
	{{/isContainer}}
	{{#isContainer}}
    {{#isMap}}
    if (pt.get_child_optional("{{baseName}}")) {
        m_{{{name}}} = fromPt<{{{dataType}}}>(pt.get_child("{{baseName}}"));
    }
    {{/isMap}}
    {{^isMap}}
    {{^isModelContainer}}
	// push all items of {{name}} into member
	if (pt.get_child_optional("{{baseName}}")) {
        m_{{{name}}} = fromPt<{{{dataType}}}>(pt.get_child("{{baseName}}"));
	}
    {{/isModelContainer}}
    {{#isModelContainer}}
    // generate new {{complexType}} Object for each item and assign it to the current
    if (pt.get_child_optional("{{baseName}}")) {
        m_{{{name}}} = fromPt<{{{dataType}}}>(pt.get_child("{{baseName}}"));
    }
    {{/isModelContainer}}
    {{/isMap}}
	{{/isContainer}}
	{{/vars}}
}

{{#isEnum}}
std::string {{classname}}::toString() const {
    return boost::lexical_cast<std::string>(getEnumValue());
}

void {{classname}}::fromString(const std::string& str) {
    setEnumValue(boost::lexical_cast<{{{dataType}}}>(str));
}

{{{dataType}}} {{classname}}::getEnumValue() const {
    return m_{{{name}}}EnumValue;
}

void {{classname}}::setEnumValue(const {{{dataType}}}& val) {
    static const std::array<{{#complexType}}{{{.}}}{{/complexType}}{{^complexType}}{{{dataType}}}{{/complexType}}, {{#allowableValues}}{{#enumVars}}{{#-last}}{{-index}}{{/-last}}{{/enumVars}}{{/allowableValues}}> allowedValues = {
        {{#allowableValues}}{{#enumVars}}{{^isNumeric}}"{{/isNumeric}}{{{value}}}{{^isNumeric}}"{{/isNumeric}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}
    };
    if (std::find(allowedValues.begin(), allowedValues.end(), val) != allowedValues.end()) {
        m_{{{name}}}EnumValue = val;
    } else {
        throw std::runtime_error("Value " + boost::lexical_cast<std::string>(val) + " not allowed");
    }
}
{{/isEnum}}
{{#vars}}
{{{dataType}}} {{classname}}::{{getter}}() const
{
    return m_{{name}};
}

void {{classname}}::{{setter}}({{{dataType}}} value)
{
	{{#isEnum}}
    static const std::array<{{#complexType}}{{{.}}}{{/complexType}}{{^complexType}}{{{dataType}}}{{/complexType}}, {{#allowableValues}}{{#enumVars}}{{#-last}}{{-index}}{{/-last}}{{/enumVars}}{{/allowableValues}}> allowedValues = {
        {{#allowableValues}}{{#enumVars}}{{^isNumeric}}"{{/isNumeric}}{{{value}}}{{^isNumeric}}"{{/isNumeric}}{{^-last}}, {{/-last}}{{/enumVars}}{{/allowableValues}}
    };

	{{#isContainer}}
    {{#isMap}}
    for (const auto &v: value) {
        if (std::find(allowedValues.begin(), allowedValues.end(), v.first) == allowedValues.end()) {
            throw std::runtime_error("Value " + boost::lexical_cast<std::string>(v.first) + " not allowed");
        }
    }
    m_{{name}} = value;
    {{/isMap}}
    {{^isMap}}
    for (const auto &v: value) {
        if (std::find(allowedValues.begin(), allowedValues.end(), v) == allowedValues.end()) {
            throw std::runtime_error("Value " + boost::lexical_cast<std::string>(v) + " not allowed");
        }
    }
    {{/isMap}}
    {{/isContainer}}
	{{^isContainer}}
    if (std::find(allowedValues.begin(), allowedValues.end(), value) != allowedValues.end()) {
		m_{{name}} = value;
	} else {
		throw std::runtime_error("Value " + boost::lexical_cast<std::string>(value) + " not allowed");
	}
    {{/isContainer}}
    {{/isEnum}}
    {{^isEnum}}
    m_{{name}} = value;
    {{/isEnum}}
}


{{/vars}}

std::vector<{{classname}}> create{{classname}}VectorFromJsonString(const std::string& json)
{
    std::stringstream sstream(json);
    boost::property_tree::ptree pt;
    boost::property_tree::json_parser::read_json(sstream,pt);

    auto vec = std::vector<{{{classname}}}>();
    for (const auto& child: pt) {
        vec.emplace_back({{{classname}}}(child.second));
    }

    return vec;
}

{{#modelNamespaceDeclarations}}
}
{{/modelNamespaceDeclarations}}

{{/model}}
{{/models}}
